package com.budwk.app.oss.config;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.obs.services.ObsClient;
import com.obs.services.model.*;
import lombok.extern.slf4j.Slf4j;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;

@Slf4j
@IocBean
public class HuaweiyunOss {

    @Inject
    private PropertiesProxy propertiesProxy;

    private ObsClient obsClient;

    public static final String CONFIG_KEY = "huweiyun";

    /**
     * 访问密钥AK
     */
    public String getAccessKey() {
        return propertiesProxy.get("hwyun.obs.accessKey");
    }

    /**
     * 访问密钥SK
     */
    public String getSecurityKey() {
        return propertiesProxy.get("hwyun.obs.securityKey");
    }

    /**
     * 终端节点
     */
    public String getEndPoint() {
        return propertiesProxy.get("hwyun.obs.endPoint");
    }

    /**
     * 桶
     */
    public String getBucketName() {
        return propertiesProxy.get("hwyun.obs.bucketName");
    }

    /**
     * 创建ObsClient实例
     * https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0202.html
     * 建议整个代码工程全局使用一个ObsClient客户端，只在程序初始化时创建一次，因为创建多个ObsClient客户端在高并发场景下会影响性能。
     * 在使用临时aksk时，aksk会有过期时间，可调用ObsClient.refresh("yourAccessKey", "yourSecretKey", "yourSecurityToken")刷新ObsClient的aksk，不必重新创建ObsClient。
     * ObsClient是线程安全的，可在并发场景下使用。
     *
     * @return
     */
    public ObsClient getInstance() {
        if (obsClient == null) {
            synchronized (HuaweiyunOss.class) {
                if (obsClient == null) {
                    obsClient = new ObsClient(getAccessKey(), getSecurityKey(), getEndPoint());
                }
            }
        }
        return obsClient;
    }

    /**
     * 上传文件
     *
     * @param file      上传文件名称
     * @param objectKey 文件路径
     * @return
     */
    public String upload(File file, String objectKey) {
        try {
            ObsClient obsClient = getInstance();
            InputStream inputStream = new FileInputStream(file);
            PutObjectRequest request = new PutObjectRequest(getBucketName(), objectKey, inputStream);
            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentLength((long) inputStream.available());
            request.setMetadata(objectMetadata);
            // 设置对象访问权限为公共读
            request.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
            // 设置文件头下载文件，不做在线预览 add by LJT 2023-12-21
            request.setUserHeaders(new HashMap<String, String>() {{put("Content-Disposition", "attachment");}});
            obsClient.putObject(request);
            //返回带有时间限制的url
            return getUploadUrl(obsClient, objectKey);
        } catch (Exception e) {
            log.info("文件上传失败：{}", e.getMessage(), e);
        }
        return null;
    }

    public String upload(byte[] bytes, String objectKey) {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
        try {
            ObsClient obsClient = getInstance();
            PutObjectRequest request = new PutObjectRequest(getBucketName(), objectKey, inputStream);
            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentLength((long) inputStream.available());
            request.setMetadata(objectMetadata);
            // 设置对象访问权限为公共读
            request.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
            // 设置文件头下载文件，不做在线预览 add by LJT 2023-12-21
            request.setUserHeaders(new HashMap<String, String>() {{put("Content-Disposition", "attachment");}});
            obsClient.putObject(request);
            //返回带有时间限制的url
            return getUploadUrl(obsClient, objectKey);
        } catch (Exception e) {
            log.info("文件上传失败：{}", e.getMessage(), e);
        }
        return null;
    }

    /**
     * 删除指定文件
     *
     * @param objectKey 文件名
     * @return
     */
    public boolean delete(String objectKey) {
        ObsClient obsClient = getInstance();
        DeleteObjectResult result = obsClient.deleteObject(getBucketName(), objectKey);
        return result.isDeleteMarker();
    }

    /**
     * 下载文件返回流式
     *
     * @param objectKey 文件名称/文件路径
     * @return
     */
    public InputStream download(String objectKey) {
        if (StrUtil.isBlank(objectKey)) return null;
        //如果objectKey第一个字符是 ”/“,则要去掉，不然无法下载
        if (objectKey.charAt(0) == '/') {
            objectKey = objectKey.substring(1);
        }
        ObsClient obsClient = getInstance();
        ObsObject obsObject = obsClient.getObject(getBucketName(), objectKey);
        return obsObject.getObjectContent();
    }

    /**
     * 获取上传地址
     *
     * @param objectKey 文件路径(文件名)
     * @return
     */
    private String getUploadUrl(ObsClient obsClient, String objectKey) {
        // URL有效期，单位毫秒
        long expireSeconds = 599999999L;
        TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.GET, expireSeconds);
        request.setBucketName(getBucketName());
        request.setObjectKey(objectKey);
        TemporarySignatureResponse response = obsClient.createTemporarySignature(request);
        return response.getSignedUrl();
    }

    /**
     * 文件存放路径
     *
     * @param suffix 文件后缀名
     * @return 结果
     */
    public static String getFileName(String suffix) {
        suffix = suffix.replace(".", "");    // 为了保证数据统一，传入的所有的后缀名都先去掉.
        // 生成uuid
        String uuid = IdUtil.fastSimpleUUID();
        // 文件路径
        String path = new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + "/" + uuid;
        return path + "." + suffix;
    }

    /**
     * 文件存放路径
     *
     * @param suffix   文件后缀名
     * @param fileName 原文件名(包含了后缀名)
     * @return 结果
     */
    public static String getFileName(String suffix, String fileName) {
        fileName = fileName.replace(suffix, ""); // 避免fileName中含有后缀 注意和下面这句话的顺序最好不要调整
        suffix = suffix.replace(".", ""); // 传入的后缀需要去掉.
        // 生成uuid
        String uuid = IdUtil.fastSimpleUUID();
        // 文件路径
        String path = new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + "/" + fileName + "-" + uuid;
        return path + "." + suffix;
    }

    /**
     * 文件存放路径
     *
     * @param suffix   文件后缀名
     * @param fileName 原文件名(包含了后缀名)
     * @return 结果
     */
    public static String getFileNameNotUUID(String suffix, String fileName) {
        fileName = fileName.replace(suffix, ""); // 避免fileName中含有后缀 注意和下面这句话的顺序最好不要调整
        suffix = suffix.replace(".", ""); // 传入的后缀需要去掉.
        // 文件路径
        String path = new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + "/" + fileName;
        return path + "." + suffix;
    }
}